// Raw IRC circapi.cpp example for use with circapi (circapi.sourceforge.net)
// this is similar to telnet, but ping responces are automated

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <circapi.h>
#include <pthread.h>

unsigned char EXIT;

int irc_socket;

void *keyboard_input_loop(void *v)
{
	char *p;
	char buff[8192];
	
	p=buff;
	while(1)
	{
		sleep(1);
		
		// get input text
		if(EXIT >= 1)
		{
			return NULL;
		}
		
		memset(buff, '\0', sizeof(buff));
		p=buff;
		
		while((*p = getchar()) != EOF && *p != '\n')
		{
			++p;
		}
		
		*p = '\r';
		++p;
		*p = '\n';
		++p;
		*p = '\0';
		
		if(!strncmp(buff, "COMMAND FORCEQUIT", 17))
		{
			EXIT=1;
			return NULL;
		}
		else if(!strncmp(buff, "COMMAND QUIT", 12))
		{
			irc_send_server_text(&irc_socket, "quit :exited terminal\r\n");
			EXIT=1;
			return NULL;
		}
		else if(irc_send_server_text(&irc_socket, buff) <= 0)
		{
			fprintf(stderr, "Unable to send text, try again? [%s]\n", buff);
		}
	}
}

void *irc_input_loop(void *v)
{
	struct ircofs ofs;
	char buff[8192];
	int attempt, x, y;
	
	puts("Connecting...");
	for(attempt=0; attempt < 5
		&& (x=irc_connect(&irc_socket, ((char **)v)[1], ((char **)v)[2], ((char **)v)[3], "guest", 1, ((char **)v)[4], 0)) <= 0; ++attempt)
	{
		printf("Could not connect to %s:%s, trying again.\n", ((char **)v)[1], ((char **)v)[2]);
	}
		
	if(x <= 0) 
	{
		fputs("Unable to connect.", stderr);
		++EXIT;
		return NULL;
	}
	
	while(1)
	{
		sleep(1);
		memset(buff, '\0', sizeof(buff));
		if(EXIT == 1) return NULL;
		if(irc_get_server_text(&irc_socket, buff, sizeof(buff)) > 0)
		{
			irc_ircofs_init(&ofs);	// initialise the IRC (O)rganized (F)ormatted (S)tring structure
			irc_buff2ircofs(&ofs, buff);	// convert the buffer containing text from the server into ofs format
			for(x=0; x<ofs.line_ct; ++x)
			{
				if(!(y=dispatch(ofs.ln[x]))) // if there's an error processing a message, it will display it
				{
					fprintf(stderr, "***[Client Error]*** Unable to process line %d!\n"
						"***[Client Error Details]*** [%s]\n", x, ofs.ln[x]);
				}
				else if(y == -1)	// dispatch will return -1 when a disconnect message is recieved
				{
					puts("***[Server Disconnected Us!]***");
					return NULL;
				}
			}
		}
	}
}

int main(int argc, char *argv[], char *env[])
{
	pthread_t kbl, ircl;
	int retval1, retval2;
	
	EXIT=0;
	if(argc <= 4)
	{
		printf("Useage:\n\n\t%s <server hostname> <server port> <nick> <real name>\n", argv[0]);
		return 0;
	}
	
	retval1 = pthread_create(&kbl, NULL, &keyboard_input_loop, (void *)argv);
	retval2 = pthread_create(&ircl, NULL, &irc_input_loop, (void *)argv);
	
	pthread_join(kbl, NULL);
	pthread_join(kbl, NULL);
	
	return 0;
}

int dispatch(char *line)
{
	// This function will scan through each line of text recieved from the server and
	// perform functions on each line accordingly (process pings, user part/quit messages, etc...

	// some buffers to use for formatting and temperary string storage
	char buff[16384];
	char buff2[16384];
	char buff3[16384];

	char nick[64];
	char nick2[64];
	char ip[64];
	char channel[64];

	switch(irc_translate(line))
	{
		case PING_MSG:		// This is a ping request from the server, formulate a ping responce & send it
			memset(buff, '\0', sizeof(buff));
			irc_ping_responce(line, buff);
			if(irc_send_server_text(&irc_socket, buff) <= 0)
			{
				fputs("Warning: unable to send PING responce!", stderr);
			}
			else
			{
				puts("PING? PONG!");
			}
			return 1;
			break;

		case NOTICEAUTH_MSG:
			printf("NOTICE AUTH: %s\n", notice_auth_message_index_message(line));
			return 1;
			break;

		case WELCOME_MSG:
			printf("The server welcomes you (%s)\n", welcome_message_index_message(line));
			return 1;
			break;

		case YOURHOST_MSG:
			printf("Your Host: (%s)\n", yourhost_message_index_message(line));
			return 1;
			break;

		case CREATED_MSG:
			printf("Server Creation: (%s)\n", created_message_index_message(line));
			return 1;
			break;

		case MYINFO_MSG:
			printf("Your Info: (%s)\n", myinfo_message_index_message(line));
			return 1;
			break;

		case BOUNCE_MSG:
			printf("Bounce Message: (%s)\n", bounce_message_index_message(line));
			return 1;
			break;

		case DISCONNECT_MSG:
			irc_disconnect(&irc_socket);
			return -1;
			break;

		case CHANNEL_PRIVMSG_MSG:
			irc_sender_message(line, buff2);
			irc_sender_nick(line, nick);
			irc_sender_ip(line, ip);
			irc_sender_channel(line, channel);
			printf("[%s] [%s] [%s]: %s\n", channel, ip, nick, buff2);
			return 1;
			break;

		case USER_PRIVMSG_MSG:
			irc_sender_message(line, buff2);
			irc_sender_nick(line, nick);
			irc_sender_ip(line, ip);
			printf("*** PRIVATE MESSAGE *** FROM [%s] [%s]: %s", ip, nick, buff2);
			return 1;
			break;

		case MODE_CHANGE_MSG:
			break;

		case USER_JOIN_MSG:
			puts(join_message_index_nick(line));
			return 1;
			break;

		case USER_PART_MSG:
			puts(part_message_index_nick(line));
			break;

		case USER_QUIT_MSG:
			puts(quit_message_index_nick(line));
			return 1;
			break;

		case CHANNEL_NAMES_MSG:
			index2string(channel, nick_list_index_channel(line));
			index2string(buff2, nick_list_index_first_nick(line));
			printf("***Current Users in Channel %s***\n%s\n", channel, buff2);
			return 1;
			break;

		case CHANNEL_NAMES_COMPLETE_MSG:
			return 1;
			break;

		case CHANNEL_JOIN_REJECT_MSG:
			index2string(channel, join_reject_index_channel(line));
			printf("***Unable to join channel %s***\n", channel);
			return 1;
			break;

		case USER_NICK_CHANGE_MSG:
			index2string(nick2, nick_change_index_oldnick(line));
			index2string(nick, nick_change_index_newnick(line));
			printf("***%s has changed his nick to %s\n", nick2, nick);
			return 1;
			break;

		case MOTD_MSG:
			printf("MOTD: %s\n", motd_index_message(line));
			return 1;
			break;

		case MOTD_END_MSG:
			printf("***MOTD COMPLETE (%s) ***\n", motd_end_index_message(line));
			return 1;
			break;

		case NOTICE_MSG:
			printf("***NOTICE*** [%s]\n", notice_index_message(line));
			return 1;
			break;

		case UNKNOWN_MSG:
			printf("***UNKNOWN MESSAGE!!*** (Write this down so I can impliment it!)\n%s\n***END OF UNKNOWN***\n",line);
			return 1;
			break;

		default:
			break;
	}

	return 0;
}




